package com.qx.grainmall.elasticsearch.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.qx.common.utils.PageUtils;
import com.qx.common.vo.SkuEsModel;
import com.qx.grainmall.elasticsearch.service.EsService;
import com.qx.grainmall.elasticsearch.vo.Goods;
import com.qx.grainmall.elasticsearch.vo.QueryVo;
import com.qx.grainmall.elasticsearch.vo.SearchParam;
import com.qx.grainmall.elasticsearch.vo.SearchResult;
import org.apache.commons.lang.StringUtils;
import org.apache.lucene.search.TotalHits;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.ml.job.results.Bucket;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.NestedQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.nested.NestedAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.aggregations.metrics.Avg;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.lang.management.LockInfo;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Service
public class EsServiceImpl implements EsService {


    @Autowired
    RestHighLevelClient client;

    @Override
    public PageUtils<Goods> toeslist(QueryVo vo, int page, int size) throws Exception {
        //构建条件
        SearchRequest searchRequest = new SearchRequest("1901a");
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        //构建好 bool查询
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        if (vo.getTitle() != null && !vo.getTitle().equals("")) {
            boolQueryBuilder.filter(QueryBuilders.matchQuery("title", vo.getTitle()));
        }
        if (vo.getEndPrice() != null) {
            boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").gte(vo.getEndPrice()));
        }
        if (vo.getStartPrice() != null) {
            boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").lte(vo.getStartPrice()));
        }

        //高亮
        if (vo.getTitle() != null && !vo.getTitle().equals("")) {
            HighlightBuilder highlightBuilder = new HighlightBuilder();
            HighlightBuilder.Field highlightTitle =
                    new HighlightBuilder.Field("title");
            highlightTitle.highlighterType("unified");
            highlightBuilder.field(highlightTitle);
            highlightBuilder.preTags("<span style='color:red'>"); //前缀
            highlightBuilder.postTags("</span>"); //后缀
            searchSourceBuilder.highlighter(highlightBuilder);
        }
        searchSourceBuilder.query(boolQueryBuilder);
        //根据 价格 倒叙排序
        searchSourceBuilder.sort("price", SortOrder.DESC);
        //分页
        searchSourceBuilder.from((page = 1) * size);
        searchSourceBuilder.size(size);

        //当前商品涉及到的所有品牌信息
        ArrayList<Goods> list = new ArrayList<>();

        searchRequest.source(searchSourceBuilder);
        SearchResponse searchResponse = null;
        searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
        SearchHits hits = searchResponse.getHits();
        TotalHits totalHits = hits.getTotalHits();
// the total number of hits, must be interpreted in the context of totalHits.relation
        long numHits = totalHits.value;
// whether the number of hits is accurate (EQUAL_TO) or a lower bound of the total (GREATER_THAN_OR_EQUAL_TO)
        TotalHits.Relation relation = totalHits.relation;
        float maxScore = hits.getMaxScore();
        SearchHit[] searchHits = hits.getHits();
        for (SearchHit hit : searchHits) {
            String index = hit.getIndex();
            String id = hit.getId();
            float score = hit.getScore();
            String sourceAsString = hit.getSourceAsString();
            //JSON 格式化对象
            Goods goods = JSON.parseObject(sourceAsString, new TypeReference<Goods>() {
            });
            goods.setId(id);
            //判断是是否按照 关键字检索 若是就显示高亮 否则不显示
            if (vo.getTitle() != null && !vo.getTitle().equals("")) {
                //拿到高亮信息 显示标题
                goods.setTitle(hit.getHighlightFields().get("title").fragments()[0].string());
            }
            list.add(goods);
        }
        return new PageUtils<Goods>(list, (int) numHits, page, size);
    }

    /**
     * 批量添加
     *
     * @param list
     * @return
     */
    @Override
    public Boolean addBulk(List<SkuEsModel> list) throws IOException {
        //构建request
        BulkRequest request = new BulkRequest();
        for (SkuEsModel skuEsModel : list) {
            String s = JSON.toJSONString(skuEsModel);
            request.add(new IndexRequest("product1901a").id(skuEsModel.getSkuId().toString())
                    .source(s, XContentType.JSON));
        }
        //执行添加
        BulkResponse bulkResponse = client.bulk(request, RequestOptions.DEFAULT);

        //判断是否执行出错
        if (bulkResponse.hasFailures()) {
            return false;
        }
        return true;
    }

    //es查询
    @Override
    public SearchResult getSearchResult(SearchParam searchParam) throws IOException {
        //构建查询条件
        SearchRequest searchRequest = new SearchRequest("product1901a");
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        //1.1 keyword
        if(!StringUtils.isBlank(searchParam.getKeyword())){
            boolQueryBuilder.must(QueryBuilders.matchQuery("skuTitle",searchParam.getKeyword()));
        }
        //1.2 分类查找
        if(searchParam.getCatalog3Id()!=null){
            boolQueryBuilder.filter(QueryBuilders.termQuery("catalogId",searchParam.getCatalog3Id()));
        }
        //1.3 品牌查找
        if(searchParam.getBrandId() !=null && searchParam.getBrandId().size()>0){
            boolQueryBuilder.filter(QueryBuilders.termsQuery("brandId",searchParam.getBrandId()));
        }
        // 1.4价格区间 1000_9000 1000_   _9000
        if(!StringUtils.isBlank(searchParam.getSkuPrice()) && searchParam.getSkuPrice().contains("_")){
            String[] s = searchParam.getSkuPrice().split("_");
            if(searchParam.getSkuPrice().startsWith("_")){
                // _9000  s[1] = 9000
                boolQueryBuilder.filter(QueryBuilders.rangeQuery("skuPrice").lte(Integer.parseInt(s[1])));
            }else if(searchParam.getSkuPrice().endsWith("_")){
                // 1000_
                boolQueryBuilder.filter(QueryBuilders.rangeQuery("skuPrice").gte(Integer.parseInt(s[0])));
            }else{
                boolQueryBuilder.filter(QueryBuilders.rangeQuery("skuPrice")
                        .gte(Integer.parseInt(s[0])).lte(Integer.parseInt(s[1])));
            }
        }
        // 1.5 是否有库存
        boolQueryBuilder.filter(QueryBuilders.termQuery("hasStock",searchParam.isHasStock()));

        // 1.6聚合查找
        if(searchParam.getAttrs() !=null&& searchParam.getAttrs().size()>0){
            //"7_麒麟880:麒麟990"
            //"2_2020"
            for (String aa :searchParam.getAttrs()){
                // aa "7_麒麟880:麒麟990"
                String[] s = aa.split("_");

                List<String> attrs = searchParam.getAttrs();
                //String path, QueryBuilder query, ScoreMode scoreMode
                BoolQueryBuilder bq = QueryBuilders.boolQuery();
                // s[0] = 7
                bq.must(QueryBuilders.termQuery("attrs.attrId",s[0]));
                // s[1]
                if(s[1].contains(":")){
                    String[] split = s[1].split(":");
                    bq.must(QueryBuilders.termsQuery("attrs.attrValue",split));
                }else{
                    bq.must(QueryBuilders.termsQuery("attrs.attrValue",s[1]));
                }
                NestedQueryBuilder nestedQueryBuilder = QueryBuilders.nestedQuery("attrs", bq, ScoreMode.None);
                boolQueryBuilder.filter(nestedQueryBuilder);
            }

        }

        searchSourceBuilder.query(boolQueryBuilder);

        //分页
        searchSourceBuilder.from((searchParam.getPageNum()-1)*searchParam.getSize());
        searchSourceBuilder.size(searchParam.getSize());
        //排序 skuPrice_desc
        if(!StringUtils.isBlank(searchParam.getSort())){
            String[] s = searchParam.getSort().split("_");
            if(s[1].equals("desc")||s[1].equals("DESC")){
                searchSourceBuilder.sort(s[0],SortOrder.DESC);
            }else{
                searchSourceBuilder.sort(s[0],SortOrder.ASC);
            }
        }
        //高亮
        if(!StringUtils.isBlank(searchParam.getKeyword())){
            HighlightBuilder highlightBuilder = new HighlightBuilder();
            HighlightBuilder.Field highlightTitle =
                    new HighlightBuilder.Field("skuTitle");
            highlightTitle.highlighterType("unified");
            highlightBuilder.preTags("<span style='color:red'>");
            highlightBuilder.postTags("</span>");
            highlightBuilder.field(highlightTitle);

            searchSourceBuilder.highlighter(highlightBuilder);
        }
        //=================2.0分组===========================
        // 2.1品牌分组
        TermsAggregationBuilder brandaggregation = AggregationBuilders.terms("brandAgg")
                .field("brandId");
        brandaggregation.subAggregation(AggregationBuilders.terms("brandNameAgg")
                .field("brandName"));
        brandaggregation.subAggregation(AggregationBuilders.terms("brandImgAgg")
                .field("brandImg.keyword"));
        searchSourceBuilder.aggregation(brandaggregation);

        //2.2 分类分组
        TermsAggregationBuilder catalogAggregation = AggregationBuilders.terms("catalogAgg")
                .field("catalogId");
        catalogAggregation.subAggregation(AggregationBuilders.terms("catalogNameAgg")
                .field("catalogName"));
        searchSourceBuilder.aggregation(catalogAggregation);


        // 2.3 规格参数分组封装
        NestedAggregationBuilder nested = AggregationBuilders.nested("attr", "attrs");
        TermsAggregationBuilder attrIdAggregation = AggregationBuilders.terms("attrIdAgg")
                .field("attrs.attrId");
        attrIdAggregation.subAggregation(AggregationBuilders.terms("attrNameAgg")
                .field("attrs.attrName.keyword"));
        attrIdAggregation.subAggregation(AggregationBuilders.terms("attrValueAgg")
                .field("attrs.attrValue"));

        nested.subAggregation(attrIdAggregation);
        searchSourceBuilder.aggregation(nested);

        //把查询语句放入request中
        searchRequest.source(searchSourceBuilder);
        //执行
        SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);


        SearchHits hits = searchResponse.getHits();
        TotalHits totalHits = hits.getTotalHits();
// the total number of hits, must be interpreted in the context of totalHits.relation
        long numHits = totalHits.value;
// whether the number of hits is accurate (EQUAL_TO) or a lower bound of the total (GREATER_THAN_OR_EQUAL_TO)
        TotalHits.Relation relation = totalHits.relation;
        float maxScore = hits.getMaxScore();
        SearchHit[] searchHits = hits.getHits();

        List<SkuEsModel> skuEsModels = new ArrayList<>();

        for (SearchHit hit : searchHits) {
            // do something with the SearchHit
            String index = hit.getIndex();
            String id = hit.getId();
            float score = hit.getScore();
            String sourceAsString = hit.getSourceAsString();
            //Json转对象
            SkuEsModel skuEsModel = JSON.parseObject(sourceAsString, new TypeReference<SkuEsModel>() {
            });
            skuEsModel.setSkuId(Long.parseLong(id));//赋值主键

            //处理高亮
            if(!StringUtils.isBlank(searchParam.getKeyword())){
                Map<String, HighlightField> highlightFields = hit.getHighlightFields();
                HighlightField highlight = highlightFields.get("skuTitle");
                Text[] fragments = highlight.fragments();
                String fragmentString = fragments[0].string();
                skuEsModel.setSkuTitle(fragmentString);//赋值高亮
            }

            skuEsModels.add(skuEsModel);
        }

        //2.0处理分组数据=======================
        // 2.1 处理品牌分组数据
        Aggregations aggregations = searchResponse.getAggregations();
        Terms brandAgg = aggregations.get("brandAgg");
        List<? extends Terms.Bucket> buckets = brandAgg.getBuckets();

        List<SearchResult.BrandVo> brands = new ArrayList<>();

        for (Terms.Bucket bucket : buckets) {
            //创建品牌vo
            SearchResult.BrandVo brandVo = new SearchResult.BrandVo();
            String brandId = bucket.getKeyAsString();
            //获取品牌图片
            Terms brandImgTerm = bucket.getAggregations().get("brandImgAgg");
//            Terms.Bucket imgbuk = brandImgTerm.getBuckets().get(0);
//            String img = imgbuk.getKeyAsString();
            //获取品牌名称
            Terms brandNameTerm= bucket.getAggregations().get("brandNameAgg");
            String brandName = brandNameTerm.getBuckets().get(0).getKeyAsString();

            brandVo.setBrandId(Long.parseLong(brandId));
//            brandVo.setBrandImg(img);
            brandVo.setBrandName(brandName);
            brands.add(brandVo);
        }
        // 2.2 处理分类分组数据
        Terms catalogAgg = aggregations.get("catalogAgg");
        List<? extends Terms.Bucket> catalogbuckets = catalogAgg.getBuckets();

        List<SearchResult.CatalogVo> catalogs = new ArrayList<>();

        for (Terms.Bucket bucket : catalogbuckets) {
            //创建分类id
            SearchResult.CatalogVo catalogVo = new SearchResult.CatalogVo();
            String catalogId = bucket.getKeyAsString();
            //获取分类名称
            Terms catalogNameTerms = bucket.getAggregations().get("catalogNameAgg");
            String catalogName = catalogNameTerms.getBuckets().get(0).getKeyAsString();
            catalogVo.setCatalogId(Long.parseLong(catalogId));
            catalogVo.setCatalogName(catalogName);
            catalogs.add(catalogVo);
        }

        // 2.3 处理规格参数分组数据
        ParsedNested attr = aggregations.get("attr"); //获取nested
        //拿到attrIdAgg
        Terms attrIdAgg = attr.getAggregations().get("attrIdAgg");
        List<? extends Terms.Bucket> attrbuckets = attrIdAgg.getBuckets();

        List<SearchResult.AttrVo> attrVos = new ArrayList<>();

        for (Terms.Bucket bucket : attrbuckets) {
            //创建attrvo
            SearchResult.AttrVo attrVo = new SearchResult.AttrVo();
            String attrId = bucket.getKeyAsString();
            //获取attrName
            Terms attrNameTerm = bucket.getAggregations().get("attrNameAgg");
//            Terms.Bucket attrNamebuk = attrNameTerm.getBuckets().get(0);
//            String attrName = attrNamebuk.getKeyAsString();
            //获取品牌名称
            Terms attrValueTerm = bucket.getAggregations().get("attrValueAgg");
            List<? extends Terms.Bucket> buckets1 = attrValueTerm.getBuckets();
            ArrayList<String> attrValues = new ArrayList<>();
            for (Terms.Bucket bucket1 : buckets1) {
                String attrValue = bucket1.getKeyAsString();
                attrValues.add(attrValue);
            }
            attrVo.setAttrId(Long.parseLong(attrId));
//            attrVo.setAttrName(attrName);
            attrVo.setAttrValue(attrValues);
            attrVos.add(attrVo);
        }



        //封装查询结果
        SearchResult searchResult = new SearchResult();
        searchResult.setProducts(skuEsModels);//赋值查询到的所有商品信息
        searchResult.setPageNum(searchParam.getPageNum());//当前页码
        searchResult.setTotal(numHits);//总记录数
        // 总记录数/size有余数 +1  无余数 总记录数/size
        int totalPages = (int) (numHits%(searchParam.getSize())==0?numHits/searchParam.getSize():numHits/searchParam.getSize()+1);
        searchResult.setTotalPages(totalPages);//总页码

        //*****************agg聚合信息分析出的*********************
        searchResult.setBrands(brands);
        searchResult.setCatalogs(catalogs);
        searchResult.setAttrs(attrVos);
        System.out.println(searchResult);

        return searchResult;
    }
}
